/* NoX (NoC Simulator)
 *
 * Dept. of Computer Science & Engineering, Pennsylvania State University.
 * All Rights Reserved.
 *  
 * 1. License     
 * NoX is distributed free of charge for academic, educational, noncommercial 
 * research purposes as long as this notice in its entirety is preserved in
 * every file included in this package.
 * All commercial use of this program requires separate licence. Contact the
 * author for details.
 * 
 * 2. All the publications that used the simulation results generated by the 
 * NoX should notify the author of the publication information and put 
 * following reference.
 *
 *  http://www.cse.psu.edu/~dpark/nox/
 * 
 * 3. Modification of the source code is permitted and encouraged as long as 
 * it follows the terms described in this copyright notice.
 *
 * 4. The author is not responsible for any problems caused by possible errors
 * of the NoX package. Therefore, users should verify the simulation result
 * before using it in their publication.
 *
 * Dept. of Computer Science & Engineering, Pennsylvania State University.
 * Contact: dpark@cse.psu.edu 
 * 
 * 6. If problems are found with the NoX package, please send an email to the
 * author for discussion and correction.

 */

/* Update History
 *
 * Jan. 31, 2006  Version 1.0 released by Dongkook Park 
 *
 */

/* FLIT_MOVE.C - Implementation of the mailbox for FLIT send/recv/read. */

#include <stdio.h>
#include <stdlib.h>
#include <cassert>
#include "router.h"
#include "flit_move.h"
#include "shared.h"
#include "main.h"

// NOTE: 'num_diff' below indicates flit movements at current simulation cycle. 
// It is updated at the end of each simulation cycle by update_cnt() function.
// This is to ensure the cycle-accurate behavior. 
// For detail, please refer to the flit_move.h file.

void send_flit(mbox_t *dest, flit_t **flit_ptr)
{
  mbox_item_t *new_item;
  
  if(flit_ptr == NULL) { fprintf(stderr,"NULL flit is sent.\n"); exit(1); }
  
  // Create new_item
  new_item = (mbox_item_t *)calloc(1,sizeof(mbox_item_t));
  if(new_item == NULL) { fprintf(stderr,"Cannot allocate memory for mbox.\n"); exit(1); }
  new_item->ptr = *flit_ptr;
  new_item->next = NULL;

  // Attach this new_item at the end of the list.
  dest->num_diff += 1; 

  if(dest->next == NULL)
    // if the dest has no messages, set dest->next to the new_item.
    dest->next = new_item;
  else
    // if the dest already has some messages, set dest->tail->next to the new_item.
    dest->tail->next = new_item;

  // set dest->tail to the newly added item.
  dest->tail = new_item;

  *flit_ptr = NULL;
}


void read_flit(mbox_t *src, flit_t **flit_ptr)
{
  *flit_ptr = src->next->ptr;
}


void recv_flit(mbox_t *src, flit_t **flit_ptr)
{
  mbox_item_t *tmp;

  *flit_ptr = src->next->ptr;
  tmp = src->next;

  src->next = tmp->next;

  // If all the messages are received, set tail to NULL.
  if(src->next == NULL)
    src->tail = NULL;
    
  src->num_diff -= 1;
  free(tmp);
}

void print_mbox( mbox_t *mbox)
{
  //return;
  mbox_item_t *tmp;
  flit_t *flit_ptr;
  tmp = mbox->next;
  while(tmp != NULL)
  {
    flit_ptr = tmp->ptr;
    printf("flit:%d(%s)(T:%c) -->", flit_ptr->flit_num, 
        (HEAD_FLIT)?"HEAD":(TAIL_FLIT)?"TAIL":"MIDDLE",flit_ptr->msgtype == CONTROL ? 'C':'D'); 
    fflush(stdout);
    tmp = tmp->next;
    if(tmp == NULL)
      printf("Last was tail\n");
  }

}

bool is_mbox_atomic( mbox_t *mbox)
{
  mbox_item_t *tmp;
  flit_t *flit_ptr, *next_flit_ptr;
  tmp = mbox->next;
  int packets=0;

  if(tmp != NULL)
    packets = 1;

  while(tmp!= NULL && tmp->next != NULL)
  {
    flit_ptr = tmp->ptr;
    next_flit_ptr = tmp->next->ptr;
    if(flit_ptr->packet_id != next_flit_ptr->packet_id)
      packets++;
    tmp = tmp->next;
  }
  return packets> 1 ? false: true;
}

void mark_mbox( int local, int n, mbox_t *mbox, int *marking_counter)
{
  //return;
  mbox_item_t *tmp;
  flit_t *flit_ptr;
  tmp = mbox->next;
  int loc=0;
  while(tmp != NULL)
  {
    flit_ptr = tmp->ptr;

    if(local != 1)
      n = flit_ptr->data.snode;

    if(marking_counter[n] < marking_cap[n])
    {
      flit_ptr->marking_weight = 2;
      flit_ptr->batch_id = current_batch[n];
      if(verbose == YES)
        printf("Marking flit:%d at mbox at loc:%d counter:%d\n",flit_ptr->flit_num,loc,marking_counter[n]);

      if(local == 1)
        marking_counter[n]++;
      else if(IS_TAIL_FLIT(flit_ptr))
        marking_counter[n]++;
    }
    else
    {
      flit_ptr->marking_weight = 1;
      flit_ptr->batch_id = -1;
    }
    tmp = tmp->next;
    loc++;
  }
}

void mbox_update_icycles( mbox_t *mbox, short id, short type, int router_or_nic)
{
  //return;
  mbox_item_t *tmp;
  flit_t *flit_ptr;
  tmp = mbox->next;
  while(tmp != NULL)
  {
    flit_ptr = tmp->ptr;
    if(flit_ptr->priority_id != id)
    {
      if(router_or_nic == 2 && flit_ptr->inj_tm-flit_ptr->msg_inj_tm < flit_ptr->pos)
      { tmp = tmp->next;
        continue;
      }
      if(type == 1)
       flit_ptr->interference_cycles++;
      else
       flit_ptr->buff_full_interference_cycles++;

    }
    /*if(flit_ptr->flit_num == 1)
      printf("flit:%d(%s) updating icycles to:%d at clock:%lld\n",flit_ptr->flit_num, 
          (HEAD_FLIT)?"HEAD":(TAIL_FLIT)?"TAIL":"MIDDLE", flit_ptr->interference_cycles, sim_clock);
    */
    tmp = tmp->next;
  }
}

int msg_cnt(mbox_t *mbox)
{
  return mbox->num_msg;
}

void update_cnt(mbox_t *mbox)
{
  mbox->num_msg += mbox->num_diff;
  mbox->num_diff=0;
}

int mbox_count_batch_flits(mbox_t *mbox,int BatchId)
{
  mbox_item_t *tmp;
  int sum=0;

  flit_t *flit_ptr;
  tmp = mbox->next;
  while(tmp != NULL)
  {
    flit_ptr = tmp->ptr;
    if(flit_ptr->batch_id == BatchId)
      sum++;
    tmp = tmp->next;
  }
  return sum;
}

int CountBatchFlits(int BatchId)
{
  int node, pc, vc;
  int sum=0;

  for(node=0; node<NUM_NODES; node++)
  {
    NUM_PC     = router_info[node].num_pc;
    NUM_NIC_PC = router_info[node].num_nic_pc;
    NUM_VC     = router_info[node].num_vc;
    
    for(pc=0; pc<NUM_PC; pc++)
      for(vc=0; vc<NUM_VC; vc++)
      {
        sum +=mbox_count_batch_flits(&(router_input_buf[node][pc][vc]),BatchId);
        if(pc < NUM_NIC_PC)
          sum += mbox_count_batch_flits(&(nic_output_buf[node][pc][vc]),BatchId);
      }//vc & pc
  }//node

  return sum;
}

int mbox_priority_present(mbox_t *mbox,int Priority)
{
  if(mbox->next != NULL)
  {
    flit_t *flit_ptr = mbox->next->ptr;
    if(flit_ptr->priority == Priority)
      return 1;
  }
  return 0;
}

int mbox_priority(mbox_t *mbox)
{
  if(mbox->next != NULL)
  {
    flit_t *flit_ptr = mbox->next->ptr;
    return(flit_ptr->priority);
  }
  return -1;
}

int CountRouterOccupancy(int node)
{
  int pc, vc;
  int sum=0;
  mbox_t *mbox;

  NUM_PC     = router_info[node].num_pc;
  NUM_NIC_PC = router_info[node].num_nic_pc;
  NUM_VC     = router_info[node].num_vc;

  for(pc=0; pc<NUM_PC; pc++)
    for(vc=0; vc<NUM_VC; vc++)
    {
      mbox = &(router_input_buf[node][pc][vc]);
      sum += mbox->num_msg ? 1 : 0;
    }//vc & pc

  return sum;
}

int CountRouterOccupancyPerPort(int node, int pc)
{
  int vc;
  int sum=0;
  mbox_t *mbox;

  NUM_PC     = router_info[node].num_pc;
  NUM_NIC_PC = router_info[node].num_nic_pc;
  NUM_VC     = router_info[node].num_vc;

    for(vc=0; vc<NUM_VC; vc++)
    {
      mbox = &(router_input_buf[node][pc][vc]);
      sum += mbox->next != NULL ? 1 : 0;
    }//vc & pc

  return sum;
}

int CountPriorityPackets(int Priority)
{
  int node, pc, vc;
  int sum=0;

  for(node=0; node<NUM_NODES; node++)
  {
    NUM_PC     = router_info[node].num_pc;
    NUM_NIC_PC = router_info[node].num_nic_pc;
    NUM_VC     = router_info[node].num_vc;
    
    for(pc=0; pc<NUM_PC; pc++)
      for(vc=0; vc<NUM_VC; vc++)
      {
        sum +=mbox_priority_present(&(router_input_buf[node][pc][vc]),Priority);
      }//vc & pc
  }//node

  return sum;
}
